package com.mayikt.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mayikt.entity.UserEntity;
import com.mayikt.utils.MayiktJwtUtils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

/**
 * 验证用户名密码正确后，生成一个token，并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法
 * attemptAuthentication ：接收并解析用户凭证。
 * successfulAuthentication ：用户成功登录后，这个方法会被调用，我们在这个方法里生成token。
 *
 * @author zhaoxinguo on 2017/9/12.
 */

public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
    // 不需要自动装配，在SecurityConfi中new JWTLoginFilter(authenticationManager())
    private AuthenticationManager authenticationManager;

    public JWTLoginFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        super.setFilterProcessesUrl("/auth/login");
    }

    // 接收并解析用户凭证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
        // 调用MemberUserDetailsService进行账号密码登录
        try {
            // 获取从前端传到后台的UserEntity对象
            UserEntity user = new ObjectMapper().readValue(req.getInputStream(), UserEntity.class);
            // 这里利用前端传过来的UserEntity对象生成一个未验证的token，内部调用loadUserByUsername获取UserDetails来对该token进行验证，
            // 验证失败抛出异常，Spring将异常捕获后交给了unsuccessfulAuthentication来处理，unsuccessfulAuthentication又交给了AuthenticationFailureHandler来处理
            // 如果认证成功，会调用successfulAuthentication，该方法中，先执行了SecurityContextHolder.getContext().setAuthentication(authResult)，再调用successHandler
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
        } catch (IOException e) {
            logger.error(e.getMessage());
            return null;
        }
    }

    // 用户成功登录后，这个方法会被调用，我们在这个方法里生成token
    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException {
        // 这里没有SecurityContextHolder.getContext().setAuthentication(auth)，而是在下次请求授权的时候再执行
        UserEntity userEntity = (UserEntity) auth.getPrincipal();
        String token = MayiktJwtUtils.generateJsonWebToken(userEntity);
        res.addHeader("token", token);
        System.out.println(token);
    }

    // 用户登陆失败
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        System.out.println(failed.getMessage());
        response.getWriter().write("authentication failed, reason: " + failed.getMessage());
    }
}
